package com.njit.WeStudy.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.njit.WeStudy.WeStudyApplication;
import com.njit.WeStudy.config.NeedLogin;
import com.njit.WeStudy.dataobject.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 用于接收客户端发送信息和发送给客户端信息
 * @author: yuruiqiao
 * @date: 2020/2/24
 */
@ServerEndpoint("/websocket/{username}")
@Controller
public class WebSocketController {
    /**
     * 发送消息  -- messageType 1代表上线 2代表下线 3代表在线名单  4代表消息
     */
    private static final int onlineType = 1;
    private static final int outlineType = 2;
    private static final int onlineUserList = 3;
    private static final int commonType = 4;
    /**
     * @description: 打印日志信息
     */
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * @description: id作为键,Session作为值,便于分发消息
     */
    private static Map<Integer, Session> clients = new ConcurrentHashMap<>();
    /**
     * @description: 存储所有的在线用户,id作为键,User作为值
     */
    private static Map<Integer,User> onlineUsers = new ConcurrentHashMap<>();
    /**
     * @description: 基于redis存贮的HttpSession,不同于Session
     */
    private static HttpSession httpSession;

    /**
     * @description: 存储当前进入的用户
     */
    private static User currentUser;

    /**
     *   进入聊天室 --> 项目中读取用户信息获取用户名
     */
    @RequestMapping("/websocket")
    @NeedLogin
    public String webSocket(ModelMap model,HttpSession httpSession) {
        currentUser = (User) httpSession.getAttribute("user");
        WebSocketController.httpSession = httpSession;
        //项目中请定义在配置文件 -->或直接读取服务器，ip 端口,80端口默认不写
        String path="ws://localhost/websocket/";
        model.addAttribute("path",path);
        return "socket";
    }

    /**
     * 监听连接
     * session 发生变化
     */
    @OnOpen
    public void onOpen(Session session) {
        //如果用户已经有了,就不需要再添加
        if(!onlineUsers.containsKey(currentUser.getUserId())){
            onlineUsers.put(currentUser.getUserId(),currentUser);
        }
        int userId = currentUser.getUserId();
        String currentUserName = currentUser.getUserName();
        //输出 webSocket 信息
        logger.info("现在来连接的客户id："+userId + "用户名：" + currentUserName);
        logger.info("有新连接加入!当前在线人数为:" + onlineUsers.size());
        try {
            //把自己的信息加入到map当中去，this=当前类（把当前类作为对象保存起来）
            clients.put(userId, session);
            List<String> userNames = new ArrayList<>();
            for (User user : onlineUsers.values()) {
                userNames.add(user.getUserName());
            }
            // 先给所有人发送通知，说我上线了
            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
            Map<String, Object> map1 = new HashMap<>();
            //  把所有用户列表
            map1.put("userNames", userNames);
            //  返回上线状态
            map1.put("messageType", onlineType);
            //  返回用户名
            map1.put("currentUserName", currentUser.getUserName());
            //  发送全体信息（用户上线信息）
            sendMessageAll(JSON.toJSONString(map1));

            // 给自己发一条消息：告诉自己现在都有谁在线
            Map<String, Object> map2 = new HashMap<>();
            map2.put("messageType", onlineUserList);
            //把所有用户放入map2
            map2.put("userNames", userNames);
            // 消息发送指定人（所有的在线用户信息）
            sendMessageTo(JSON.toJSONString(map2), currentUser.getUserName());
        } catch (IOException e) {
            logger.info(currentUser.getUserName() + "上线的时候通知所有人发生了错误");
        }
    }


    /**
     * 监听连接断开
     */
    @OnClose
    public void onClose() {
        //所有在线用户中去除下线用户
        User newUser = (User) httpSession.getAttribute("user");
        clients.remove(newUser.getUserId());
        onlineUsers.remove(newUser.getUserId());
        try {
            Map<String, Object> map1 = new HashMap<>();
            map1.put("messageType", outlineType);
            //所有在线用户
            map1.put("onlineUsers", clients.keySet());
            //下线用户的用户名
            map1.put("username", newUser.getUserName());
            //发送信息，所有人，通知谁下线了
            sendMessageAll(JSON.toJSONString(map1));
        } catch (IOException e) {
            logger.info(newUser.getUserName() + "下线的时候通知所有人发生了错误");
        }
        logger.info("有连接关闭！ 当前在线人数" + onlineUsers.size());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("sessionId为"+session.getId()+"操作时,服务端发生了错误" + error.getMessage());
    }

    /**
     * 监听消息
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            logger.info("来自客户端消息：" + message);
            //用户发送的信息
            JSONObject jsonObject = JSON.parseObject(message);
            //发送的内容
            String content = jsonObject.getString("content");
            //发送人
            String fromUser = jsonObject.getString("fromUser");
            //接收人
            String targetUser = jsonObject.getString("targetUser");

            Map<String, Object> map1 = new HashMap<>();
            map1.put("messageType", commonType);
            map1.put("content", content);
            map1.put("fromUser", fromUser);
            if (targetUser.equals("All")) {
                //消息发送所有人（同步）
                map1.put("targetUser", "所有人");
                sendMessageAll(JSON.toJSONString(map1));
            } else {
                //消息发送指定人（同步）
                map1.put("targetUser", targetUser);
                sendMessageTo(JSON.toJSONString(map1), targetUser);
            }
        } catch (Exception e) {
            logger.info("sessionId为"+session.getId()+"发生了错误");
        }
    }

    /**
     *  消息发送指定人
     */
    private void sendMessageTo(String message, String ToUserName) throws IOException {
        //遍历所有用户
        for (Session session : clients.values()) {
            if (currentUser.getUserName().equals(ToUserName)) {
                //消息发送指定人（同步）
                session.getBasicRemote().sendText(message);
                break;
            }
        }
    }

    /**
     *  消息发送所有人
     */
    private void sendMessageAll(String message) throws IOException {
        for (Session session : clients.values()) {
            //消息发送所有人(同步)getAsyncRemote
            session.getBasicRemote().sendText(message);
        }
    }



    /*
     *  注解说明
     *  @MessageMapping(value = "/chat")   // 匹配客户端 send 消息时的URL
     *  @SendTo("/topic/getResponse")      //用于给客户端订阅广播消息
     *  @SendToUser(value = "/personal")   //用于给客户端订阅点对点消息；
     *  @Payload：使用客户端 STOMP 帧的 body 赋值
     *  @Header(“xxx”)：使用客户端 STOMP 帧的 headers 中的 xxx 赋值
     *
     **/

/*
 * 广播推送
 */
//    @MessageMapping(value = "/chat") // 匹配客户端 send 消息时的URL
//    @SendTo("/topic/getResponse")   //分别用于给客户端订阅广播消息
//    public String talk(@Payload String text, @Header("simpSessionId") String sessionId) throws Exception {
//        return "【" + sessionId + "】说:【" + text + "】";
//    }

/*
 * 点对点推送
 */

//    @MessageMapping(value = "/speak")  // 匹配客户端 send 消息时的URL
//    @SendToUser(value = "/personal")   //分别用于给客户端订阅点对点消息；
//    public String speak(@Payload String text, @Header("simpSessionId") String sessionId) throws Exception {
//        return text;
//    }


/*
 * 异常信息推送
 */

//    @MessageExceptionHandler
//    @SendToUser(value = "/errors")
//    public String handleException(Throwable exception) {
//        return exception.getMessage();
//    }

}
